<template>
  <div class="k8s-cluster-manager">
    <!-- 头部：新建集群按钮 + 搜索框 + 刷新按钮 + 列选择按钮 -->
    <el-row class="header-row">
      <el-col :span="24">
        <el-card class="header-card" shadow="never" :body-style="{ padding: '10px' }">
          <div class="header-actions">
            <el-button
                size="small"
                type="primary"
                icon="el-icon-edit"
                @click="createk8sDrawer = true"
                class="create-btn"
            >
              新建集群
            </el-button>

            <!-- 列选择按钮 -->
            <el-button
                size="small"
                type="primary"
                plain
                icon="el-icon-menu"
                @click="handleColumnSelectClick"
                class="column-select-btn"
            >
              选择列
            </el-button>

            <div class="search-container">
              <el-input
                  class="search-input"
                  size="small"
                  clearable
                  placeholder="搜索集群名称"
                  v-model="searchValue"
                  @clear="getk8slist"
                  @keyup.enter.native="getk8slist"
              ></el-input>

              <el-button
                  size="small"
                  type="primary"
                  plain
                  icon="el-icon-refresh"
                  @click="getk8slist()"
                  class="refresh-btn"
              >
                刷新
              </el-button>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <!-- 列选择弹窗 -->
    <el-dialog
        title="表格列选择"
        v-model="showColumnSelectDialog"
        width="400px"
        :close-on-click-modal="false"
        @close="saveColumnConfig"
    >
      <el-form>
        <el-form-item label="请选择需要显示的列：">
          <el-checkbox-group v-model="selectedColumns">
            <el-checkbox
                v-for="(column, key) in tableColumns"
                :key="key"
                :label="key"
                class="column-checkbox"
                :disabled="key === 'action'"
            >
              {{ column.label }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showColumnSelectDialog = false">取消</el-button>
        <el-button type="primary" @click="confirmColumnSelect">确认</el-button>
      </template>
    </el-dialog>

    <!-- 集群数据表格：动态渲染列 -->
    <el-row class="table-row">
      <el-col :span="24">
        <el-card class="table-card" shadow="never" :body-style="{ padding: '10px' }">
          <el-table
              :data="filteredK8sList"
              v-loading="appLoading"
              border
              stripe
              class="cluster-table"
              :height="tableHeight"
          >
            <!-- 动态渲染选中的列 -->
            <template v-for="(key) in selectedColumns" :key="key">
              <!-- ID列 -->
              <el-table-column
                  v-if="key === 'id'"
                  :label="tableColumns.id.label"
                  :prop="tableColumns.id.prop"
                  :align="tableColumns.id.align"
                  :width="tableColumns.id.width"
              ></el-table-column>

              <!-- 集群名称列 -->
              <el-table-column
                  v-if="key === 'k8s_name'"
                  :label="tableColumns.k8s_name.label"
                  :prop="tableColumns.k8s_name.prop"
                  :align="tableColumns.k8s_name.align"
                  :width="tableColumns.k8s_name.width"
              ></el-table-column>

              <!-- 操作系统版本列 -->
              <el-table-column
                  v-if="key === 'osVersion'"
                  :label="tableColumns.osVersion.label"
                  :prop="tableColumns.osVersion.prop"
                  :align="tableColumns.osVersion.align"
                  :width="tableColumns.osVersion.width"
              ></el-table-column>

              <!-- K8s版本列 -->
              <el-table-column
                  v-if="key === 'k8sVersion'"
                  :label="tableColumns.k8sVersion.label"
                  :prop="tableColumns.k8sVersion.prop"
                  :align="tableColumns.k8sVersion.align"
                  :width="tableColumns.k8sVersion.width"
              ></el-table-column>

              <!-- 高可用类型列 -->
              <el-table-column
                  v-if="key === 'haType'"
                  :label="tableColumns.haType.label"
                  :prop="tableColumns.haType.prop"
                  :align="tableColumns.haType.align"
                  :width="tableColumns.haType.width"
              >
                <template v-slot="scope">
                  <el-tag type="info">{{ scope.row.haType || '无' }}</el-tag>
                </template>
              </el-table-column>

              <!-- 高可用IP列 -->
              <el-table-column
                  v-if="key === 'physicalHaIp'"
                  :label="tableColumns.physicalHaIp.label"
                  :prop="tableColumns.physicalHaIp.prop"
                  :align="tableColumns.physicalHaIp.align"
                  :width="tableColumns.physicalHaIp.width"
              ></el-table-column>

              <!-- 网络组件列 -->
              <el-table-column
                  v-if="key === 'networkComponent'"
                  :label="tableColumns.networkComponent.label"
                  :prop="tableColumns.networkComponent.prop"
                  :align="tableColumns.networkComponent.align"
                  :width="tableColumns.networkComponent.width"
              ></el-table-column>

              <!-- 容器运行时列 -->
              <el-table-column
                  v-if="key === 'containerRuntime'"
                  :label="tableColumns.containerRuntime.label"
                  :prop="tableColumns.containerRuntime.prop"
                  :align="tableColumns.containerRuntime.align"
                  :width="tableColumns.containerRuntime.width"
              ></el-table-column>

              <!-- 镜像仓库列 -->
              <el-table-column
                  v-if="key === 'imageRepo'"
                  :label="tableColumns.imageRepo.label"
                  :prop="tableColumns.imageRepo.prop"
                  :align="tableColumns.imageRepo.align"
                  :width="tableColumns.imageRepo.width"
              >
                <template v-slot="scope">
                  <el-tooltip effect="dark" :content="scope.row.imageRepo" placement="top">
                    <div class="ellipsis">{{ scope.row.imageRepo }}</div>
                  </el-tooltip>
                </template>
              </el-table-column>

              <!-- Master节点列 -->
              <el-table-column
                  v-if="key === 'clusterMasterIps'"
                  :label="tableColumns.clusterMasterIps.label"
                  :align="tableColumns.clusterMasterIps.align"
                  :width="tableColumns.clusterMasterIps.width"
              >
                <template v-slot="scope">
                  <div v-for="(ip, index) in scope.row.clusterMasterIps" :key="index" class="ip-item">
                    {{ ip }}
                  </div>
                </template>
              </el-table-column>

              <!-- Node节点列 -->
              <el-table-column
                  v-if="key === 'clusterNodeIps'"
                  :label="tableColumns.clusterNodeIps.label"
                  :align="tableColumns.clusterNodeIps.align"
                  :width="tableColumns.clusterNodeIps.width"
              >
                <template v-slot="scope">
                  <div v-if="scope.row.clusterNodeIps && scope.row.clusterNodeIps.length">
                    <div v-for="(ip, index) in scope.row.clusterNodeIps" :key="index" class="ip-item">
                      {{ ip }}
                    </div>
                  </div>
                  <div v-else class="text-gray">无</div>
                </template>
              </el-table-column>

              <!-- 部署机节点列 -->
              <el-table-column
                  v-if="key === 'clusterDeployIps'"
                  :label="tableColumns.clusterDeployIps.label"
                  :align="tableColumns.clusterDeployIps.align"
                  :width="tableColumns.clusterDeployIps.width"
              >
                <template v-slot="scope">
                  <div v-if="scope.row.clusterDeployIps && scope.row.clusterDeployIps.length">
                    <div v-for="(ip, index) in scope.row.clusterDeployIps" :key="index" class="ip-item">
                      {{ ip }}
                    </div>
                  </div>
                  <div v-else class="text-gray">无</div>
                </template>
              </el-table-column>

              <!-- 操作列（固定显示，不允许隐藏） -->
              <el-table-column
                  v-if="key === 'action'"
                  :label="tableColumns.action.label"
                  :fixed="tableColumns.action.fixed"
                  :width="tableColumns.action.width"
                  :align="tableColumns.action.align"
              >
                <template v-slot="scope">
                  <el-button
                      size="mini"
                      icon="el-icon-edit"
                      type="success"
                      plain
                      @click="editCluster(scope.row)"
                      class="edit-btn"
                  >
                    编辑创建
                  </el-button>
                  <el-button
                      size="mini"
                      icon="el-icon-view"
                      type="primary"
                      plain
                      @click="viewClusterDetail(scope.row)"
                      class="view-btn"
                  >
                    查看配置
                  </el-button>
                  <el-button
                      size="mini"
                      icon="el-icon-delete"
                      type="danger"
                      plain
                      @click="delk8slist(scope.row.k8s_name,scope.row.id)"
                      class="delete-btn"
                  >
                    删除配置
                  </el-button>
                  <!-- 新增：查看日志按钮 -->
                  <el-button
                      size="mini"
                      icon="el-icon-document"
                      type="info"
                      plain
                      @click="openLogDialog(scope.row)"
                      class="log-btn"
                  >
                    查看日志
                  </el-button>
                </template>
              </el-table-column>
            </template>
          </el-table>

          <!-- 分页组件 -->
          <el-pagination
              class="pagination"
              background
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange"
              :current-page="currentPage"
              :page-sizes="[10, 20, 30]"
              :page-size="pagesize"
              layout="total, sizes, prev, pager, next, jumper"
              :total="filteredK8sList.length"
          ></el-pagination>
        </el-card>
      </el-col>
    </el-row>

    <!-- 集群详情弹窗 -->
    <el-dialog
        title="集群详情"
        v-model="detailDialogVisible"
        width="70%"
        :close-on-click-modal="false"
    >
      <el-row :gutter="20">
        <el-col :span="12">
          <el-card>
            <div slot="header">基本信息</div>
            <el-descriptions column="1" border>
              <el-descriptions-item label="集群ID">{{ currentCluster.id }}</el-descriptions-item>
              <el-descriptions-item label="集群名称">{{ currentCluster.k8s_name }}</el-descriptions-item>
              <el-descriptions-item label="操作系统版本">{{ currentCluster.osVersion }}</el-descriptions-item>
              <el-descriptions-item label="K8s版本">{{ currentCluster.k8sVersion }}</el-descriptions-item>
              <el-descriptions-item label="网络组件">{{ currentCluster.networkComponent }}</el-descriptions-item>
              <el-descriptions-item label="容器运行时">{{ currentCluster.containerRuntime }}</el-descriptions-item>
              <el-descriptions-item label="镜像仓库">{{ currentCluster.imageRepo }}</el-descriptions-item>
            </el-descriptions>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card>
            <div slot="header">高可用配置</div>
            <el-descriptions column="1" border>
              <el-descriptions-item label="高可用类型">{{ currentCluster.haType || '无' }}</el-descriptions-item>
              <el-descriptions-item label="高可用IP">{{ currentCluster.physicalHaIp || '无' }}</el-descriptions-item>
              <el-descriptions-item label="网络接口">{{ currentCluster.physicalHaNic || '无' }}</el-descriptions-item>
            </el-descriptions>
          </el-card>
        </el-col>
      </el-row>

      <el-row :gutter="20" style="margin-top: 20px;">
        <el-col :span="8">
          <el-card>
            <div slot="header">Master节点信息</div>
            <el-table
                :data="masterNodeTableData"
                border
                size="small"
                style="width: 100%;"
            >
              <el-table-column prop="hostname" label="主机名" align="center"></el-table-column>
              <el-table-column prop="ip" label="IP地址" align="center"></el-table-column>
              <el-table-column prop="nic" label="网卡" align="center"></el-table-column>
            </el-table>
          </el-card>
        </el-col>
        <el-col :span="8">
          <el-card>
            <div slot="header">Node节点信息</div>
            <el-table
                :data="nodeTableData"
                border
                size="small"
                style="width: 100%;"
            >
              <el-table-column prop="hostname" label="主机名" align="center"></el-table-column>
              <el-table-column prop="ip" label="IP地址" align="center"></el-table-column>
              <el-table-column prop="nic" label="网卡" align="center"></el-table-column>
            </el-table>
          </el-card>
        </el-col>
        <el-col :span="8">
          <el-card>
            <div slot="header">部署机节点信息</div>
            <el-table
                :data="deployNodeTableData"
                border
                size="small"
                style="width: 100%;"
            >
              <el-table-column prop="hostname" label="主机名" align="center"></el-table-column>
              <el-table-column prop="ip" label="IP地址" align="center"></el-table-column>
              <el-table-column prop="nic" label="网卡" align="center"></el-table-column>
            </el-table>
          </el-card>
        </el-col>
      </el-row>
    </el-dialog>

    <!-- 新建集群配置抽屉 -->
    <el-drawer
        v-model="createk8sDrawer"
        direction="rtl"
        :before-close="handleClose"
        size="90%"
        title="新建集群配置"
        class="create-drawer"
    >
      <template #default>
        <el-row type="flex" justify="center">
          <el-col :span="20">
            <el-form
                ref="createDeploymentForm"
                :rules="createK8sRules"
                :model="createk8s"
                label-width="140px"
                label-position="left"
                class="create-form"
            >
              <!-- 1. 集群基础信息 -->
              <el-form-item
                  label="集群名称"
                  prop="k8s_name"
                  class="form-item"
              >
                <el-input
                    v-model="createk8s.k8s_name"
                    placeholder="请输入集群名称（如k8s-cluster-01）"
                ></el-input>
              </el-form-item>

              <!-- 2. 系统版本选择（单选） -->
              <el-form-item
                  label="系统版本"
                  prop="osVersion"
                  class="form-item"
              >
                <el-radio-group v-model="createk8s.osVersion">
                  <el-radio label="debian">Debian 系列</el-radio>
                  <el-radio label="centos">Red Hat 系列</el-radio>
                </el-radio-group>
              </el-form-item>

              <!-- 2. k8s版本选择（下拉） -->
              <el-form-item
                  label="k8s版本"
                  prop="k8sVersion"
                  required
                  class="form-item"
                  :rules="[{ required: true, message: '请选择k8s版本', trigger: 'change' }]"
              >
                <el-select v-model="createk8s.k8sVersion" placeholder="请选择k8s版本">
                  <el-option label="1.19.16" value="1.19.16"></el-option>
                  <el-option label="1.27.11" value="1.27.11"></el-option>
                  <el-option label="1.28.2" value="1.28.2"></el-option>
                </el-select>
              </el-form-item>

              <!-- 集群部署：服务器选择（多选）+ 批量IP+网卡配置 + 高可用IP -->
              <div>
                <!-- 集群Master服务器选择（多选） -->
                <el-form-item
                    label="选择Master服务器"
                    prop="clusterServerIds"
                    class="form-item"
                >
                  <el-select
                      v-model="createk8s.clusterServerIds"
                      placeholder="请选择Master服务器"
                      multiple
                      clearable
                      style="width: 100%;"
                      :disabled="serverListLoading"
                      @change="handleClusterServerChange"
                  >
                    <el-option
                        v-for="server in serverList"
                        :key="server.id"
                        :label="`${server.hostname} (${server.ssh_ip})`"
                        :value="server.id"
                    ></el-option>
                    <el-option
                        v-if="serverList.length === 0 && !serverListLoading"
                        disabled
                        value=""
                    >
                      暂无可用服务器
                    </el-option>
                  </el-select>
                  <div v-if="serverListLoading" class="loading-tip">
                    <i class="el-icon-loading"></i> 正在加载服务器列表...
                  </div>
                  <div v-if="serverListError" class="error-tip">
                    <i class="el-icon-error"></i> 服务器列表加载失败，请稍后重试
                  </div>
                </el-form-item>

                <!-- 为每台选中的Master服务器配置IP+网卡 -->
                <div
                    v-if="createk8s.clusterServerIds.length > 0"
                    class="cluster-server-ip-config"
                >
                  <el-form-item
                      label="集群Master节点配置"
                      class="form-item"
                  >
                    <div class="config-table-container">
                      <el-table
                          :data="clusterServerIpConfigList"
                          border
                          size="small"
                          class="config-table"
                      >
                        <el-table-column
                            label="服务器名称"
                            prop="serverName"
                            width="200"
                        ></el-table-column>
                        <el-table-column label="IP+网卡选择" width="300">
                          <template v-slot="scope">
                            <el-select
                                v-model="scope.row.ipWithNic"
                                placeholder="选择IP和网卡"
                                style="width: 100%;"
                                @change="(val) => handleClusterIpNicChange(val, scope.$index)"
                            >
                              <el-option
                                  v-for="(ipInfo, idx) in scope.row.availableIpList"
                                  :key="idx"
                                  :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                  :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                              ></el-option>
                            </el-select>
                          </template>
                        </el-table-column>
                        <el-table-column label="凭据选择" width="200">
                          <template v-slot="scope">
                            <el-select
                                v-model="scope.row.credentialId"
                                placeholder="选择凭据"
                                style="width: 100%;"
                                :disabled="credentialListLoading"
                            >
                              <el-option
                                  v-for="(cred, idx) in credentialList"
                                  :key="idx"
                                  :label="cred.name"
                                  :value="cred.id"
                              ></el-option>
                              <el-option
                                  v-if="credentialList.length === 0 && !credentialListLoading"
                                  disabled
                                  value=""
                              >
                                暂无可用凭据
                              </el-option>
                            </el-select>
                          </template>
                        </el-table-column>
                        <el-table-column label="状态" width="120">
                          <template v-slot="scope">
                            <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                              {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                            </el-tag>
                          </template>
                        </el-table-column>
                      </el-table>
                    </div>
                    <div v-if="clusterConfigIncompleteError" class="validation-error">
                      请为所有选中的服务器配置IP+网卡和凭据
                    </div>
                  </el-form-item>

                </div>

                <!-- 集群Node节点配置 -->
                <el-form-item
                    label="Node节点配置"
                    class="form-item"
                >
                  <el-divider content-position="left">可选，不配置则只有Master节点</el-divider>

                  <!-- Node服务器选择（多选，排除已选Master） -->
                  <el-form-item
                      prop="clusterNodeServerIds"
                      class="form-item"
                  >
                    <el-select
                        v-model="createk8s.clusterNodeServerIds"
                        placeholder="请选择Node服务器（可选）"
                        multiple
                        clearable
                        style="width: 100%;"
                        :disabled="serverListLoading"
                        @change="handleClusterNodeServerChange"
                    >
                      <!-- 数据源为clusterNodeAvailableServers（排除已选Master集群） -->
                      <el-option
                          v-for="server in clusterNodeAvailableServers"
                          :key="server.id"
                          :label="`${server.hostname} (${server.ssh_ip})`"
                          :value="server.id"
                      ></el-option>
                      <el-option
                          v-if="clusterNodeAvailableServers.length === 0 && !serverListLoading"
                          disabled
                          value=""
                      >
                        暂无可用服务器（已选Master集群不可作为Node）
                      </el-option>
                    </el-select>
                    <div v-if="serverListLoading" class="loading-tip">
                      <i class="el-icon-loading"></i> 正在加载服务器列表...
                    </div>
                    <div v-if="serverListError" class="error-tip">
                      <i class="el-icon-error"></i> 服务器列表加载失败，请稍后重试
                    </div>
                  </el-form-item>

                  <!-- 为每台选中的Node服务器配置IP+网卡 -->
                  <div
                      v-if="createk8s.clusterNodeServerIds.length > 0"
                      class="cluster-server-ip-config"
                  >
                    <el-form-item
                        label="集群Node节点配置"
                        class="form-item"
                    >
                      <div class="config-table-container">
                        <el-table
                            :data="clusterNodeIpConfigList"
                            border
                            size="small"
                            class="config-table"
                        >
                          <el-table-column
                              label="服务器名称"
                              prop="serverName"
                              width="200"
                          ></el-table-column>
                          <el-table-column label="IP+网卡选择" width="300">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.ipWithNic"
                                  placeholder="选择IP和网卡"
                                  style="width: 100%;"
                                  @change="(val) => handleClusterNodeIpNicChange(val, scope.$index)"
                              >
                                <el-option
                                    v-for="(ipInfo, idx) in scope.row.availableIpList"
                                    :key="idx"
                                    :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                    :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="凭据选择" width="200">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.credentialId"
                                  placeholder="选择凭据"
                                  style="width: 100%;"
                                  :disabled="credentialListLoading"
                              >
                                <el-option
                                    v-for="(cred, idx) in credentialList"
                                    :key="idx"
                                    :label="cred.name"
                                    :value="cred.id"
                                ></el-option>
                                <el-option
                                    v-if="credentialList.length === 0 && !credentialListLoading"
                                    disabled
                                    value=""
                                >
                                  暂无可用凭据
                                </el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="状态" width="120">
                            <template v-slot="scope">
                              <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                                {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                              </el-tag>
                            </template>
                          </el-table-column>
                        </el-table>
                      </div>
                      <div v-if="clusterNodeConfigIncompleteError" class="validation-error">
                        请为所有选中的Node服务器配置IP+网卡和凭据
                      </div>
                    </el-form-item>
                  </div>
                </el-form-item>

                <!-- 部署机配置（必填，单台服务器） -->
                <el-form-item
                    label="部署机配置"
                    prop="clusterDeployServerId"
                    required
                    class="form-item"
                >
                  <el-divider content-position="left">必填，仅可选择一台服务器</el-divider>

                  <!-- 部署机服务器选择（单选，排除已选Master和Node） -->
                  <el-form-item
                      prop="clusterDeployServerId"
                      class="form-item"
                      :rules="[{ required: true, message: '请选择部署机', trigger: 'change' }]"
                  >
                    <el-select
                        v-model="createk8s.clusterDeployServerId"
                        placeholder="请选择部署机服务器（必填）"
                        clearable
                        style="width: 100%;"
                        :disabled="serverListLoading"
                        @change="handleClusterDeployServerChange"
                    >
                      <!-- 数据源为clusterDeployAvailableServers（排除已选Master和Node） -->
                      <el-option
                          v-for="server in clusterDeployAvailableServers"
                          :key="server.id"
                          :label="`${server.hostname} (${server.ssh_ip})`"
                          :value="server.id"
                      ></el-option>
                      <el-option
                          v-if="clusterDeployAvailableServers.length === 0 && !serverListLoading"
                          disabled
                          value=""
                      >
                        暂无可用服务器（已选Master/Node不可作为部署机）
                      </el-option>
                    </el-select>
                    <div v-if="serverListLoading" class="loading-tip">
                      <i class="el-icon-loading"></i> 正在加载服务器列表...
                    </div>
                    <div v-if="serverListError" class="error-tip">
                      <i class="el-icon-error"></i> 服务器列表加载失败，请稍后重试
                    </div>
                  </el-form-item>

                  <!-- 为选中的单台部署机配置IP+网卡 -->
                  <div
                      v-if="createk8s.clusterDeployServerId"
                      class="cluster-server-ip-config"
                  >
                    <el-form-item
                        label="部署机节点配置"
                        class="form-item"
                    >
                      <div class="config-table-container">
                        <el-table
                            :data="clusterDeployIpConfigList"
                            border
                            size="small"
                            class="config-table"
                            show-header
                        >
                          <el-table-column
                              label="服务器名称"
                              prop="serverName"
                              width="200"
                          ></el-table-column>
                          <el-table-column label="IP+网卡选择" width="300">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.ipWithNic"
                                  placeholder="选择IP和网卡"
                                  style="width: 100%;"
                                  @change="(val) => handleClusterDeployIpNicChange(val, scope.$index)"
                              >
                                <el-option
                                    v-for="(ipInfo, idx) in scope.row.availableIpList"
                                    :key="idx"
                                    :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                    :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="凭据选择" width="200">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.credentialId"
                                  placeholder="选择凭据"
                                  style="width: 100%;"
                                  :disabled="credentialListLoading"
                              >
                                <el-option
                                    v-for="(cred, idx) in credentialList"
                                    :key="idx"
                                    :label="cred.name"
                                    :value="cred.id"
                                ></el-option>
                                <el-option
                                    v-if="credentialList.length === 0 && !credentialListLoading"
                                    disabled
                                    value=""
                                >
                                  暂无可用凭据
                                </el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="状态" width="120">
                            <template v-slot="scope">
                              <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                                {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                              </el-tag>
                            </template>
                          </el-table-column>
                        </el-table>
                      </div>
                      <div v-if="clusterDeployConfigIncompleteError" class="validation-error">
                        请为部署机配置IP+网卡和凭据
                      </div>
                    </el-form-item>
                  </div>
                </el-form-item>
              </div>

              <!-- 高可用IP+网卡配置 -->
              <el-form-item
                  label="Master高可用配置"
                  class="form-item"
                  required
              >
                <!-- 高可用类型选择 -->
                <div class="ha-type-container">
                  <el-radio-group
                      v-model="createk8s.haType"
                      @change="handleHaTypeChange"
                      class="ha-type-radio-group"
                  >
                    <el-radio label="physical">使用物理高可用设备</el-radio>
                  </el-radio-group>
                </div>

                <!-- 物理高可用设备配置 -->
                <div v-if="createk8s.haType === 'physical'" class="ha-physical-config">
                  <div class="ha-physical-item">
                    <span class="sub-label">物理高可用设备IP：</span>

                    <el-form-item
                        prop="physicalHaIp"
                        :rules="[{
                              pattern: /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
                              message: 'IP地址格式不合法',
                              trigger: 'blur'
                            }]"
                        style="margin-top: 5px; margin-bottom: 0;"
                    >
                      <el-input
                          v-model="createk8s.physicalHaIp"
                          placeholder="必填，请输入物理高可用设备的IP地址"
                          style="width: 100%;"
                      ></el-input>
                    </el-form-item>
                  </div>

                  <div class="ha-physical-item">
                    <span class="sub-label">网络接口名称：</span>
                    <el-form-item
                        prop="physicalHaNic"
                        style="margin-top: 5px; margin-bottom: 0;"
                    >
                      <el-input
                          v-model="createk8s.physicalHaNic"
                          placeholder="非必填，请输入网络接口名称（如eth0、ens33）"
                          style="width: 100%;"
                      ></el-input>
                      <div class="form-hint">
                        提示：请确保所有集群服务器均可访问此IP地址
                      </div>
                    </el-form-item>
                  </div>
                </div>
              </el-form-item>

              <!-- 4. 镜像依赖自定义 -->
              <el-form-item
                  label="镜像依赖自定义"
                  prop="imageRepo"
                  class="form-item"
              >
                <el-input
                    v-model="createk8s.imageRepo"
                    placeholder="默认镜像仓库：registry.aliyuncs.com/google_containers"
                ></el-input>
                <div class="form-hint">
                  提示：若不填写，将使用默认镜像仓库
                </div>
              </el-form-item>

              <!-- 5. 网络组件（默认Cilium） -->
              <el-form-item
                  label="网络组件"
                  prop="networkComponent"
                  class="form-item"
              >
                <el-radio-group v-model="createk8s.networkComponent">
                  <el-radio label="cilium" disabled>cilium（默认）</el-radio>
                </el-radio-group>
              </el-form-item>

              <!-- 6. 容器运行时选择 -->
              <el-form-item
                  label="容器运行时"
                  prop="containerRuntime"
                  class="form-item"
              >
                <el-radio-group v-model="createk8s.containerRuntime">
                  <el-radio label="docker">docker</el-radio>
                  <el-radio label="containerd">containerd</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-form>
          </el-col>
        </el-row>
      </template>

      <!-- 抽屉底部按钮 -->
      <template #footer>
        <el-button @click="handleClose" class="cancel-btn">取消</el-button>
        <el-button type="primary" @click="submitCreateK8sForm()" class="submit-btn">立即创建</el-button>
      </template>
    </el-drawer>

    <!-- 编辑集群配置抽屉 -->
    <el-drawer
        v-model="editk8sDrawer"
        direction="rtl"
        :before-close="handleEditClose"
        size="90%"
        title="编辑集群配置"
        class="edit-drawer"
    >
      <template #default>
        <el-row type="flex" justify="center">
          <el-col :span="20">
            <el-form
                ref="editDeploymentForm"
                :rules="editK8sRules"
                :model="editk8s"
                label-width="140px"
                label-position="left"
                class="create-form"
            >
              <!-- 编辑表单内容 -->
              <el-form-item
                  label="集群名称"
                  prop="k8s_name"
                  class="form-item"
              >
                <el-input
                    v-model="editk8s.k8s_name"
                    placeholder="请输入集群名称（如k8s-cluster-01）"
                ></el-input>
              </el-form-item>

              <!-- 系统版本选择（单选） -->
              <el-form-item
                  label="系统版本"
                  prop="osVersion"
                  class="form-item"
              >
                <el-radio-group v-model="editk8s.osVersion">
                  <el-radio label="debian">Debian 系列</el-radio>
                  <el-radio label="centos">Red Hat 系列</el-radio>
                </el-radio-group>
              </el-form-item>

              <!-- k8s版本选择（下拉） -->
              <el-form-item
                  label="k8s版本"
                  prop="k8sVersion"
                  required
                  class="form-item"
                  :rules="[{ required: true, message: '请选择k8s版本', trigger: 'change' }]"
              >
                <el-select v-model="editk8s.k8sVersion" placeholder="请选择k8s版本">
                  <el-option label="1.19.16" value="1.19.16"></el-option>
                  <el-option label="1.27.11" value="1.27.11"></el-option>
                  <el-option label="1.28.2" value="1.28.2"></el-option>
                </el-select>
              </el-form-item>

              <!-- 集群部署：服务器选择（多选）+ 批量IP+网卡配置 + 高可用IP -->
              <div>
                <!-- 集群Master服务器选择（多选） -->
                <el-form-item
                    label="选择Master服务器"
                    prop="clusterServerIds"
                    class="form-item"
                >
                  <el-select
                      v-model="editk8s.clusterServerIds"
                      placeholder="请选择Master服务器"
                      multiple
                      clearable
                      style="width: 100%;"
                      :disabled="serverListLoading"
                      @change="handleEditClusterServerChange"
                  >
                    <el-option
                        v-for="server in serverList"
                        :key="server.id"
                        :label="`${server.hostname} (${server.ssh_ip})`"
                        :value="server.id"
                    ></el-option>
                    <el-option
                        v-if="serverList.length === 0 && !serverListLoading"
                        disabled
                        value=""
                    >
                      暂无可用服务器
                    </el-option>
                  </el-select>
                </el-form-item>

                <!-- 为每台选中的Master服务器配置IP+网卡 -->
                <div
                    v-if="editk8s.clusterServerIds.length > 0"
                    class="cluster-server-ip-config"
                >
                  <el-form-item
                      label="集群Master节点配置"
                      class="form-item"
                  >
                    <div class="config-table-container">
                      <el-table
                          :data="editClusterServerIpConfigList"
                          border
                          size="small"
                          class="config-table"
                      >
                        <el-table-column
                            label="服务器名称"
                            prop="serverName"
                            width="200"
                        ></el-table-column>
                        <el-table-column label="IP+网卡选择" width="300">
                          <template v-slot="scope">
                            <el-select
                                v-model="scope.row.ipWithNic"
                                placeholder="选择IP和网卡"
                                style="width: 100%;"
                                @change="(val) => handleEditClusterIpNicChange(val, scope.$index)"
                            >
                              <el-option
                                  v-for="(ipInfo, idx) in scope.row.availableIpList"
                                  :key="idx"
                                  :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                  :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                              ></el-option>
                            </el-select>
                          </template>
                        </el-table-column>
                        <el-table-column label="凭据选择" width="200">
                          <template v-slot="scope">
                            <el-select
                                v-model="scope.row.credentialId"
                                placeholder="选择凭据"
                                style="width: 100%;"
                                :disabled="credentialListLoading"
                            >
                              <el-option
                                  v-for="(cred, idx) in credentialList"
                                  :key="idx"
                                  :label="cred.name"
                                  :value="cred.id"
                              ></el-option>
                            </el-select>
                          </template>
                        </el-table-column>
                        <el-table-column label="状态" width="120">
                          <template v-slot="scope">
                            <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                              {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                            </el-tag>
                          </template>
                        </el-table-column>
                      </el-table>
                    </div>
                  </el-form-item>
                </div>

                <!-- 集群Node节点配置 -->
                <el-form-item
                    label="Node节点配置"
                    class="form-item"
                >
                  <el-divider content-position="left">可选，不配置则只有Master节点</el-divider>

                  <!-- Node服务器选择（多选，排除已选Master） -->
                  <el-form-item
                      prop="clusterNodeServerIds"
                      class="form-item"
                  >
                    <el-select
                        v-model="editk8s.clusterNodeServerIds"
                        placeholder="请选择Node服务器（可选）"
                        multiple
                        clearable
                        style="width: 100%;"
                        :disabled="serverListLoading"
                        @change="handleEditClusterNodeServerChange"
                    >
                      <el-option
                          v-for="server in clusterNodeAvailableServers"
                          :key="server.id"
                          :label="`${server.hostname} (${server.ssh_ip})`"
                          :value="server.id"
                      ></el-option>
                    </el-select>
                  </el-form-item>

                  <!-- 为每台选中的Node服务器配置IP+网卡 -->
                  <div
                      v-if="editk8s.clusterNodeServerIds.length > 0"
                      class="cluster-server-ip-config"
                  >
                    <el-form-item
                        label="集群Node节点配置"
                        class="form-item"
                    >
                      <div class="config-table-container">
                        <el-table
                            :data="editClusterNodeIpConfigList"
                            border
                            size="small"
                            class="config-table"
                        >
                          <el-table-column
                              label="服务器名称"
                              prop="serverName"
                              width="200"
                          ></el-table-column>
                          <el-table-column label="IP+网卡选择" width="300">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.ipWithNic"
                                  placeholder="选择IP和网卡"
                                  style="width: 100%;"
                                  @change="(val) => handleEditClusterNodeIpNicChange(val, scope.$index)"
                              >
                                <el-option
                                    v-for="(ipInfo, idx) in scope.row.availableIpList"
                                    :key="idx"
                                    :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                    :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="凭据选择" width="200">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.credentialId"
                                  placeholder="选择凭据"
                                  style="width: 100%;"
                              >
                                <el-option
                                    v-for="(cred, idx) in credentialList"
                                    :key="idx"
                                    :label="cred.name"
                                    :value="cred.id"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="状态" width="120">
                            <template v-slot="scope">
                              <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                                {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                              </el-tag>
                            </template>
                          </el-table-column>
                        </el-table>
                      </div>
                    </el-form-item>
                  </div>
                </el-form-item>

                <!-- 部署机配置（编辑模式，单台） -->
                <el-form-item
                    label="部署机配置"
                    prop="clusterDeployServerId"
                    required
                    class="form-item"
                >
                  <el-divider content-position="left">必填，仅可选择一台服务器</el-divider>

                  <!-- 部署机服务器选择（单选，排除已选Master和Node） -->
                  <el-form-item
                      prop="clusterDeployServerId"
                      class="form-item"
                      :rules="[{ required: true, message: '请选择部署机', trigger: 'change' }]"
                  >
                    <el-select
                        v-model="editk8s.clusterDeployServerId"
                        placeholder="请选择部署机服务器（必填）"
                        clearable
                        style="width: 100%;"
                        :disabled="serverListLoading"
                        @change="handleEditClusterDeployServerChange"
                    >
                      <el-option
                          v-for="server in clusterDeployAvailableServers"
                          :key="server.id"
                          :label="`${server.hostname} (${server.ssh_ip})`"
                          :value="server.id"
                      ></el-option>
                    </el-select>
                  </el-form-item>

                  <!-- 为选中的单台部署机配置IP+网卡 -->
                  <div
                      v-if="editk8s.clusterDeployServerId"
                      class="cluster-server-ip-config"
                  >
                    <el-form-item
                        label="部署机节点配置"
                        class="form-item"
                    >
                      <div class="config-table-container">
                        <el-table
                            :data="editClusterDeployIpConfigList"
                            border
                            size="small"
                            class="config-table"
                            show-header
                        >
                          <el-table-column
                              label="服务器名称"
                              prop="serverName"
                              width="200"
                          ></el-table-column>
                          <el-table-column label="IP+网卡选择" width="300">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.ipWithNic"
                                  placeholder="选择IP和网卡"
                                  style="width: 100%;"
                                  @change="(val) => handleEditClusterDeployIpNicChange(val, scope.$index)"
                              >
                                <el-option
                                    v-for="(ipInfo, idx) in scope.row.availableIpList"
                                    :key="idx"
                                    :label="`IP: ${ipInfo.ip} | 网卡: ${ipInfo.ip_name}`"
                                    :value="JSON.stringify({ ip: ipInfo.ip, ipName: ipInfo.ip_name })"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="凭据选择" width="200">
                            <template v-slot="scope">
                              <el-select
                                  v-model="scope.row.credentialId"
                                  placeholder="选择凭据"
                                  style="width: 100%;"
                              >
                                <el-option
                                    v-for="(cred, idx) in credentialList"
                                    :key="idx"
                                    :label="cred.name"
                                    :value="cred.id"
                                ></el-option>
                              </el-select>
                            </template>
                          </el-table-column>
                          <el-table-column label="状态" width="120">
                            <template v-slot="scope">
                              <el-tag :type="scope.row.ipWithNic && scope.row.credentialId ? 'success' : 'warning'">
                                {{ scope.row.ipWithNic && scope.row.credentialId ? '已配置' : '待配置' }}
                              </el-tag>
                            </template>
                          </el-table-column>
                        </el-table>
                      </div>
                      <div v-if="clusterDeployConfigIncompleteError" class="validation-error">
                        请为部署机配置IP+网卡和凭据
                      </div>
                    </el-form-item>
                  </div>
                </el-form-item>
              </div>

              <!-- 高可用IP+网卡配置 -->
              <el-form-item
                  label="Master高可用配置"
                  class="form-item"
                  required
              >
                <!-- 高可用类型选择 -->
                <div class="ha-type-container">
                  <el-radio-group
                      v-model="editk8s.haType"
                      @change="handleEditHaTypeChange"
                      class="ha-type-radio-group"
                  >
                    <el-radio label="physical">使用物理高可用设备</el-radio>
                  </el-radio-group>
                </div>

                <!-- 物理高可用设备配置 -->
                <div v-if="editk8s.haType === 'physical'" class="ha-physical-config">
                  <div class="ha-physical-item">
                    <span class="sub-label">物理高可用设备IP：</span>
                    <el-form-item
                        prop="physicalHaIp"
                        :rules="[{
                              pattern: /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
                              message: 'IP地址格式不合法',
                              trigger: 'blur'
                            }]"
                        style="margin-top: 5px; margin-bottom: 0;"
                    >
                      <el-input
                          v-model="editk8s.physicalHaIp"
                          placeholder="必填，请输入物理高可用设备的IP地址"
                          style="width: 100%;"
                      ></el-input>
                    </el-form-item>
                  </div>

                  <div class="ha-physical-item">
                    <span class="sub-label">网络接口名称：</span>
                    <el-form-item
                        prop="physicalHaNic"
                        style="margin-top: 5px; margin-bottom: 0;"
                    >
                      <el-input
                          v-model="editk8s.physicalHaNic"
                          placeholder="非必填，请输入网络接口名称（如eth0、ens33）"
                          style="width: 100%;"
                      ></el-input>
                    </el-form-item>
                  </div>
                </div>
              </el-form-item>

              <!-- 镜像依赖自定义 -->
              <el-form-item
                  label="镜像依赖自定义"
                  prop="imageRepo"
                  class="form-item"
              >
                <el-input
                    v-model="editk8s.imageRepo"
                    placeholder="默认镜像仓库：registry.aliyuncs.com/google_containers"
                ></el-input>
              </el-form-item>

              <!-- 网络组件 -->
              <el-form-item
                  label="网络组件"
                  prop="networkComponent"
                  class="form-item"
              >
                <el-radio-group v-model="editk8s.networkComponent">
                  <el-radio label="cilium" disabled>cilium（默认）</el-radio>
                </el-radio-group>
              </el-form-item>

              <!-- 容器运行时选择 -->
              <el-form-item
                  label="容器运行时"
                  prop="containerRuntime"
                  class="form-item"
              >
                <el-radio-group v-model="editk8s.containerRuntime">
                  <el-radio label="docker">docker</el-radio>
                  <el-radio label="containerd">containerd</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-form>
          </el-col>
        </el-row>
      </template>

      <!-- 编辑抽屉底部按钮 -->
      <template #footer>
        <el-button @click="handleEditClose" class="cancel-btn">取消</el-button>
        <el-button type="primary" @click="submitEditK8sForm()" class="submit-btn" >保存修改</el-button>
        <el-button type="success" @click="submitEditK8sForm(1)" class="submit-btn" :disabled="isSubmitting" >保存并创建</el-button>
      </template>
    </el-drawer>

    <!-- 日志查看弹窗 -->
    <el-dialog
        title="部署日志实时查看"
        v-model="logDialogVisible"
        width="90%"
        height="80vh"
        :close-on-click-modal="false"
        :before-close="handleLogDialogClose"
    >
      <div class="log-dialog-container">
        <!-- 日志控制区 -->
        <div class="log-controls">
          <el-input
              v-model="logSearchKey"
              placeholder="搜索日志内容..."
              size="small"
              clearable
              class="log-search"
              @input="filterLogs"
          ></el-input>
          <el-button
              size="small"
              type="primary"
              plain
              @click="clearLogs"
              class="log-btn"
          >
            清空日志
          </el-button>
          <el-button
              size="small"
              type="primary"
              plain
              @click="copyAllLogs"
              class="log-btn"
          >
            复制全部
          </el-button>
          <el-button
              size="small"
              type="primary"
              :plain="!autoScroll"
              @click="toggleAutoScroll"
              class="log-btn"
          >
            {{ autoScroll ? '关闭自动滚动' : '开启自动滚动' }}
          </el-button>
        </div>

        <!-- 日志展示区 -->
        <div class="log-content" ref="logContent">
          <div
              v-for="(log, index) in filteredLogs"
              :key="index"
              :class="['log-line', log.isError ? 'log-error' : '']"
              v-show="!logSearchKey || log.content.toLowerCase().includes(logSearchKey.toLowerCase())"
          >
            <span class="log-time">[{{ formatTime(log.timestamp) }}]</span>
            <span class="log-text">{{ log.content }}</span>
          </div>
          <div v-if="logs.length === 0" class="log-empty">等待日志输出...</div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import common from '../../common/Config'
import httpClient from '@/request'

export default {
  name: 'K8sClusterManager',
  data() {
    return {
      // 集群列表相关
      k8sList: [],
      searchValue: '',
      appLoading: false,
      tableHeight: 'calc(100vh - 240px)',

      // 分页相关
      currentPage: 1,
      pagesize: 10,

      // 抽屉控制
      createk8sDrawer: false,
      editk8sDrawer: false,          // 编辑抽屉控制

      // 详情弹窗
      detailDialogVisible: false,
      currentCluster: {},
      masterNodeTableData: [],
      nodeTableData: [],
      deployNodeTableData: [], // 部署机详情数据

      // 服务器列表相关
      serverList: [],          // 原始服务器列表（包含完整信息）
      serverListLoading: false,// 加载状态
      serverListError: false,  // 加载错误标记

      // 凭据列表相关
      credentialList: [],      // 凭据列表
      credentialListLoading: false, // 凭据加载状态
      credentialListError: false,   // 凭据加载错误

      // 新建集群表单数据
      isSubmitting: false,

      createk8s: {
        hostname: [],
        node_hostname: [],
        deploy_hostname: '', // 部署机主机名（单台）
        k8s_name: '',
        osVersion: 'centos',
        k8sVersion: '',
        // 集群部署相关
        clusterServerIds: [],      // 集群部署选择的服务器ID列表
        haType: 'physical',        // 高可用类型：使用物理高可用设备
        physicalHaIp: "",      // 物理高可用设备IP，默认为空字符串
        physicalHaNic: "",     // 物理高可用设备网络接口，默认为空字符串
        // 集群部署Node相关
        clusterNodeServerIds: [],   // 集群Node服务器ID列表
        // 部署机相关（单台）
        clusterDeployServerId: '',  // 部署机服务器ID（单台）
        // 通用配置
        imageRepo: 'registry.aliyuncs.com/google_containers',
        networkComponent: 'cilium',
        containerRuntime: 'docker'
      },

      // 编辑集群表单数据
      editk8s: {
        id: '',
        hostname: [],
        node_hostname: [],
        deploy_hostname: '', // 部署机主机名（单台）
        k8s_name: '',
        osVersion: 'centos',
        k8sVersion: '',
        clusterServerIds: [],
        haType: 'physical',
        physicalHaIp: "",
        physicalHaNic: "",
        clusterNodeServerIds: [],
        clusterDeployServerId: '',  // 部署机服务器ID（单台）
        imageRepo: 'registry.aliyuncs.com/google_containers',
        networkComponent: 'cilium',
        containerRuntime: 'docker'
      },
      editingCluster: null,          // 正在编辑的集群数据

      // 集群服务器IP配置列表（用于集群部署）
      clusterServerIpConfigList: [],
      // 集群Node服务器IP配置列表
      clusterNodeIpConfigList: [],
      // 部署机IP配置列表（单台）
      clusterDeployIpConfigList: [],
      // 编辑时的服务器配置列表
      editClusterServerIpConfigList: [],
      editClusterNodeIpConfigList: [],
      editClusterDeployIpConfigList: [], // 编辑时部署机配置列表（单台）

      // 表单校验规则
      createK8sRules: {
        k8s_name: [
          { required: true, message: '请输入集群名称', trigger: 'blur' },
          { min: 2, max: 30, message: '长度在2-30个字符之间', trigger: 'blur' }
        ],
        osVersion: [
          { required: true, message: '请选择系统版本', trigger: 'change' }
        ],
        clusterServerIds: [
          { required: true, message: '请选择集群服务器', trigger: 'change' }
        ],
        clusterDeployServerId: [ // 部署机必填校验（单台）
          { required: true, message: '请选择部署机', trigger: 'change' }
        ],
        physicalHaIp: [
          {
            required: true,
            message: '物理高可用设备IP为必填项',
            trigger: ['blur', 'change']
          },
          {
            pattern: /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
            message: 'IP地址格式不合法（例：192.168.1.1）',
            trigger: 'blur'
          }
        ],
        imageRepo: [
          {
            pattern: /^[a-zA-Z0-9_\-\.\/:]*$/,
            message: '镜像仓库格式不合法',
            trigger: 'blur'
          }
        ],
        networkComponent: [
          { required: true, message: '请选择网络组件', trigger: 'change' }
        ],
        containerRuntime: [
          { required: true, message: '请选择容器运行时', trigger: 'change' }
        ]
      },
      // 编辑表单校验规则（与新建类似）
      editK8sRules: {
        k8s_name: [
          { required: true, message: '请输入集群名称', trigger: 'blur' },
          { min: 2, max: 30, message: '长度在2-30个字符之间', trigger: 'blur' }
        ],
        osVersion: [
          { required: true, message: '请选择系统版本', trigger: 'change' }
        ],
        clusterServerIds: [
          { required: true, message: '请选择集群服务器', trigger: 'change' }
        ],
        clusterDeployServerId: [ // 部署机必填校验（单台）
          { required: true, message: '请选择部署机', trigger: 'change' }
        ],
        physicalHaIp: [
          {
            required: true,
            message: '物理高可用设备IP为必填项',
            trigger: ['blur', 'change']
          },
          {
            pattern: /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
            message: 'IP地址格式不合法（例：192.168.1.1）',
            trigger: 'blur'
          }
        ],
        imageRepo: [
          {
            pattern: /^[a-zA-Z0-9_\-\.\/:]*$/,
            message: '镜像仓库格式不合法',
            trigger: 'blur'
          }
        ],
        networkComponent: [
          { required: true, message: '请选择网络组件', trigger: 'change' }
        ],
        containerRuntime: [
          { required: true, message: '请选择容器运行时', trigger: 'change' }
        ]
      },

      // 错误状态
      clusterConfigIncompleteError: false, // 集群配置不完整错误
      haIpError: false,                   // 高可用IP错误
      clusterNodeConfigIncompleteError: false, // 集群Node配置不完整
      clusterDeployConfigIncompleteError: false, // 部署机配置不完整

      // 列选择相关
      showColumnSelectDialog: false, // 列选择弹窗显示控制
      tableColumns: { // 表格所有列的配置，包含部署机列
        id: { label: 'ID', prop: 'id', align: 'center', width: 60 },
        k8s_name: { label: '集群名称', prop: 'k8s_name', align: 'center' },
        osVersion: { label: '操作系统版本', prop: 'osVersion', align: 'center', width: 120 },
        k8sVersion: { label: 'K8s版本', prop: 'k8sVersion', align: 'center', width: 100 },
        haType: { label: '高可用类型', prop: 'haType', align: 'center', width: 100 },
        physicalHaIp: { label: '高可用IP', prop: 'physicalHaIp', align: 'center' },
        networkComponent: { label: '网络组件', prop: 'networkComponent', align: 'center', width: 100 },
        containerRuntime: { label: '容器运行时', prop: 'containerRuntime', align: 'center', width: 120 },
        imageRepo: { label: '镜像仓库', prop: 'imageRepo', align: 'center' },
        clusterMasterIps: { label: 'Master节点', align: 'center' },
        clusterNodeIps: { label: 'Node节点', align: 'center' },
        clusterDeployIps: { label: '部署机节点', align: 'center' }, // 部署机列
        action: { label: '操作', fixed: 'right', width: 260, align: 'center' } // 加宽操作列容纳新按钮
      },
      selectedColumns: [], // 当前选中的列（默认显示所有列）

      // 日志相关
      logDialogVisible: false,       // 日志弹窗显示控制
      currentLogTaskId: '',          // 当前日志对应的任务ID
      logs: [],                      // 所有日志数据
      filteredLogs: [],              // 搜索过滤后的日志
      logSearchKey: '',              // 日志搜索关键词
      autoScroll: true,              // 是否自动滚动到底部
      ws: null,                      // WebSocket实例
      isWsConnected: false           // WebSocket连接状态
    }
  },

  computed: {
    /**
     * 根据搜索关键词过滤集群列表
     */
    filteredK8sList() {
      if (!this.searchValue) return this.k8sList;

      const keyword = this.searchValue.toLowerCase();
      return this.k8sList.filter(item =>
          item.k8s_name.toLowerCase().includes(keyword) ||
          item.physicalHaIp.includes(keyword) ||
          item.osVersion.toLowerCase().includes(keyword) ||
          item.k8sVersion.includes(keyword)
      );
    },

    /**
     * 获取所有选中集群服务器的IP列表（用于高可用IP选择）
     */
    getAllClusterServerIpList() {
      if (!this.createk8s.clusterServerIds || this.createk8s.clusterServerIds.length === 0) {
        return [];
      }

      const ipList = [];
      this.createk8s.clusterServerIds.forEach(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        if (server && server.private_ip && server.private_ip.length > 0) {
          server.private_ip.forEach(ipInfo => {
            ipList.push({
              ...ipInfo,
              serverId: server.id,
              serverName: server.hostname
            });
          });
        }
      });
      return ipList;
    },

    /**
     * 集群部署：所有选中的Node服务器IP列表
     */
    getAllClusterNodeIpList() {
      if (!this.createk8s.clusterNodeServerIds || this.createk8s.clusterNodeServerIds.length === 0) {
        return [];
      }
      const ipList = [];
      this.createk8s.clusterNodeServerIds.forEach(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        if (server && server.private_ip && server.private_ip.length > 0) {
          server.private_ip.forEach(ipInfo => {
            ipList.push({
              ...ipInfo,
              serverId: server.id,
              serverName: server.hostname
            });
          });
        }
      });
      return ipList;
    },

    /**
     * 所有选中的部署机服务器IP列表（单台）
     */
    getAllClusterDeployIpList() {
      if (!this.createk8s.clusterDeployServerId) {
        return [];
      }

      const ipList = [];
      const server = this.serverList.find(s => s.id === this.createk8s.clusterDeployServerId);
      if (server && server.private_ip && server.private_ip.length > 0) {
        server.private_ip.forEach(ipInfo => {
          ipList.push({
            ...ipInfo,
            serverId: server.id,
            serverName: server.hostname
          });
        });
      }
      return ipList;
    },

    /**
     * 集群部署Node可选服务器（排除所有已选Master）
     */
    clusterNodeAvailableServers() {
      // 编辑模式使用编辑表单的ID列表，否则使用创建表单的
      const masterIds = this.editk8sDrawer ? this.editk8s.clusterServerIds : this.createk8s.clusterServerIds;
      if (!masterIds.length) return this.serverList;
      return this.serverList.filter(server => !masterIds.includes(server.id));
    },

    /**
     * 部署机可选服务器（排除已选Master和Node）
     */
    clusterDeployAvailableServers() {
      return this.serverList;
    }
  },

  methods: {
    /**
     * 列选择弹窗控制
     */
    handleColumnSelectClick() {
      this.showColumnSelectDialog = true;
    },

    /**
     * 查看集群详情
     */
    viewClusterDetail(cluster) {
      this.currentCluster = { ...cluster };
      // 处理Master节点数据
      this.masterNodeTableData = [];
      if (cluster.hostname && cluster.clusterMasterIps && cluster.clusterMasterNics) {
        cluster.clusterMasterIps.forEach((ip, index) => {
          this.masterNodeTableData.push({
            hostname: cluster.hostname[index] || '未知',
            ip: ip,
            nic: cluster.clusterMasterNics[index] || '未知'
          });
        });
      }

      // 处理Node节点数据
      this.nodeTableData = [];
      if (cluster.node_hostname && cluster.clusterNodeIps && cluster.clusterNodeNics) {
        cluster.clusterNodeIps.forEach((ip, index) => {
          this.nodeTableData.push({
            hostname: cluster.node_hostname[index] || '未知',
            ip: ip,
            nic: cluster.clusterNodeNics[index] || '未知'
          });
        });
      }

      // 处理部署机节点数据
      this.deployNodeTableData = [];
      if (cluster.deploy_hostname && cluster.clusterDeployIps && cluster.clusterDeployNics) {
        // 部署机是单台，直接取第一个元素
        this.deployNodeTableData.push({
          hostname: cluster.deploy_hostname || '未知',
          ip: cluster.clusterDeployIps[0] || '未知',
          nic: cluster.clusterDeployNics[0] || '未知'
        });
      }

      this.detailDialogVisible = true;
    },
    /**
     * 编辑集群
     */
    editCluster(cluster) {
      this.editingCluster = { ...cluster };

      // 1. 填充基础表单数据
      this.editk8s = {
        id: cluster.id,
        k8s_name: cluster.k8s_name,
        osVersion: cluster.osVersion,
        k8sVersion: cluster.k8sVersion,
        haType: cluster.haType || 'physical',
        physicalHaIp: cluster.physicalHaIp || "",
        physicalHaNic: cluster.physicalHaNic || "",
        imageRepo: cluster.imageRepo || 'registry.aliyuncs.com/google_containers',
        networkComponent: cluster.networkComponent || 'cilium',
        containerRuntime: cluster.containerRuntime || 'docker',
        clusterServerIds: cluster.clusterServerIds || [], // 原有Master服务器ID
        clusterNodeServerIds: cluster.clusterNodeServerIds || [], // 原有Node服务器ID
        clusterDeployServerId: cluster.clusterDeployServerIds?.[0] || '', // 部署机单台ID
        hostname: cluster.hostname || [], // 原有Master主机名
        node_hostname: cluster.node_hostname || [], // 原有Node主机名
        deploy_hostname: cluster.deploy_hostname || '' // 部署机单台主机名
      };

      // 2. 回显Master节点的IP+网卡、凭据配置
      if (this.editk8s.clusterServerIds.length && cluster.clusterMasterIps && cluster.clusterMasterNics && cluster.clusterMasterCredentials) {
        this.editClusterServerIpConfigList = this.editk8s.clusterServerIds.map((serverId, index) => {
          const server = this.serverList.find(s => s.id === serverId);
          const ipWithNic = server && cluster.clusterMasterIps[index]
              ? JSON.stringify({
                ip: cluster.clusterMasterIps[index],
                ipName: cluster.clusterMasterNics[index] || ''
              })
              : '';
          return {
            serverId,
            serverName: server?.hostname || '未知服务器',
            availableIpList: server?.private_ip || [],
            ipWithNic: ipWithNic,
            credentialId: cluster.clusterMasterCredentials[index] || ''
          };
        });
      } else {
        this.handleEditClusterServerChange();
      }

      // 3. 回显Node节点的IP+网卡、凭据配置
      if (this.editk8s.clusterNodeServerIds.length && cluster.clusterNodeIps && cluster.clusterNodeNics && cluster.clusterNodeCredentials) {
        this.editClusterNodeIpConfigList = this.editk8s.clusterNodeServerIds.map((serverId, index) => {
          const server = this.serverList.find(s => s.id === serverId);
          const ipWithNic = server && cluster.clusterNodeIps[index]
              ? JSON.stringify({
                ip: cluster.clusterNodeIps[index],
                ipName: cluster.clusterNodeNics[index] || ''
              })
              : '';
          return {
            serverId,
            serverName: server?.hostname || '未知服务器',
            availableIpList: server?.private_ip || [],
            ipWithNic: ipWithNic,
            credentialId: cluster.clusterNodeCredentials[index] || ''
          };
        });
      } else {
        this.handleEditClusterNodeServerChange();
      }

      // 4. 回显部署机节点的IP+网卡、凭据配置（单台）
      if (this.editk8s.clusterDeployServerId && cluster.clusterDeployIps && cluster.clusterDeployNics && cluster.clusterDeployCredentials) {
        this.editClusterDeployIpConfigList = [
          {
            serverId: this.editk8s.clusterDeployServerId,
            serverName: this.serverList.find(s => s.id === this.editk8s.clusterDeployServerId)?.hostname || '未知服务器',
            availableIpList: this.serverList.find(s => s.id === this.editk8s.clusterDeployServerId)?.private_ip || [],
            ipWithNic: cluster.clusterDeployIps[0]
                ? JSON.stringify({
                  ip: cluster.clusterDeployIps[0],
                  ipName: cluster.clusterDeployNics[0] || ''
                })
                : '',
            credentialId: cluster.clusterDeployCredentials[0] || ''
          }
        ];
      } else {
        this.handleEditClusterDeployServerChange();
      }

      this.editk8sDrawer = true;
    },

    /**
     * 处理高可用类型切换
     */
    handleHaTypeChange() {
      if (this.createk8s.haType === 'server') {
        this.haIpError = false;
      } else {
        this.createk8s.physicalHaIp = "";
        this.createk8s.physicalHaNic = "";
      }
    },

    /**
     * 处理编辑时高可用类型切换
     */
    handleEditHaTypeChange() {
      if (this.editk8s.haType === 'server') {
        this.haIpError = false;
      } else {
        this.editk8s.physicalHaIp = "";
        this.editk8s.physicalHaNic = "";
      }
    },

    /**
     * 处理集群服务器选择变更
     */
    handleClusterServerChange() {
      this.clusterServerIpConfigList = this.createk8s.clusterServerIds.map(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        return {
          serverId,
          serverName: server?.hostname || '未知服务器',
          availableIpList: server?.private_ip || [],
          ipWithNic: '',
          credentialId: ''
        };
      });
      this.createk8s.haType = 'physical';
      this.createk8s.physicalHaIp = "";
      this.createk8s.physicalHaNic = "";

      // Master变更时，清空已选Node和部署机
      if (this.createk8s.clusterNodeServerIds.length) {
        this.createk8s.clusterNodeServerIds = [];
        this.clusterNodeIpConfigList = [];
        this.clusterNodeConfigIncompleteError = false;
      }
      if (this.createk8s.clusterDeployServerId) {
        this.createk8s.clusterDeployServerId = '';
        this.clusterDeployIpConfigList = [];
        this.clusterDeployConfigIncompleteError = false;
      }
    },

    /**
     * 处理编辑时集群服务器选择变更
     */
    handleEditClusterServerChange() {
      this.editClusterServerIpConfigList = this.editk8s.clusterServerIds.map(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        const existingConfig = this.editClusterServerIpConfigList.find(item => item.serverId === serverId);
        return {
          serverId,
          serverName: server?.hostname || '未知服务器',
          availableIpList: server?.private_ip || [],
          ipWithNic: existingConfig?.ipWithNic || '',
          credentialId: existingConfig?.credentialId || ''
        };
      });

      // 联动清空部署机中可能包含的旧Master节点
      if (this.editk8s.clusterDeployServerId && this.editk8s.clusterServerIds.includes(this.editk8s.clusterDeployServerId)) {
        this.editk8s.clusterDeployServerId = '';
        this.handleEditClusterDeployServerChange();
      }
    },

    /**
     * 处理集群部署Node服务器选择变更
     */
    handleClusterNodeServerChange() {
      this.clusterNodeIpConfigList = this.createk8s.clusterNodeServerIds.map(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        return {
          serverId,
          serverName: server?.hostname || '未知服务器',
          availableIpList: server?.private_ip || [],
          ipWithNic: '',
          credentialId: ''
        };
      });

      // Node变更时，清空部署机中可能包含的旧Node节点
      if (this.createk8s.clusterDeployServerId && this.createk8s.clusterNodeServerIds.includes(this.createk8s.clusterDeployServerId)) {
        this.createk8s.clusterDeployServerId = '';
        this.clusterDeployIpConfigList = [];
        this.clusterDeployConfigIncompleteError = false;
      }
    },

    /**
     * 处理编辑时集群Node服务器选择变更
     */
    handleEditClusterNodeServerChange() {
      this.editClusterNodeIpConfigList = this.editk8s.clusterNodeServerIds.map(serverId => {
        const server = this.serverList.find(s => s.id === serverId);
        const existingConfig = this.editClusterNodeIpConfigList.find(item => item.serverId === serverId);
        return {
          serverId,
          serverName: server?.hostname || '未知服务器',
          availableIpList: server?.private_ip || [],
          ipWithNic: existingConfig?.ipWithNic || '',
          credentialId: existingConfig?.credentialId || ''
        };
      });

      // 联动清空部署机中可能包含的旧Node节点
      if (this.editk8s.clusterDeployServerId && this.editk8s.clusterNodeServerIds.includes(this.editk8s.clusterDeployServerId)) {
        this.editk8s.clusterDeployServerId = '';
        this.handleEditClusterDeployServerChange();
      }
    },

    /**
     * 处理部署机服务器选择变更（单台）
     */
    handleClusterDeployServerChange() {
      if (!this.createk8s.clusterDeployServerId) {
        this.clusterDeployIpConfigList = [];
        return;
      }

      // 只添加当前选中的单台部署机配置
      const server = this.serverList.find(s => s.id === this.createk8s.clusterDeployServerId);
      this.clusterDeployIpConfigList = [{
        serverId: this.createk8s.clusterDeployServerId,
        serverName: server?.hostname || '未知服务器',
        availableIpList: server?.private_ip || [],
        ipWithNic: '',
        credentialId: ''
      }];
    },

    /**
     * 处理编辑时部署机服务器选择变更（单台）
     */
    handleEditClusterDeployServerChange() {
      if (!this.editk8s.clusterDeployServerId) {
        this.editClusterDeployIpConfigList = [];
        return;
      }

      // 只添加当前选中的单台部署机配置
      const server = this.serverList.find(s => s.id === this.editk8s.clusterDeployServerId);
      const existingConfig = this.editClusterDeployIpConfigList[0] || {};
      this.editClusterDeployIpConfigList = [{
        serverId: this.editk8s.clusterDeployServerId,
        serverName: server?.hostname || '未知服务器',
        availableIpList: server?.private_ip || [],
        ipWithNic: existingConfig.ipWithNic || '',
        credentialId: existingConfig.credentialId || ''
      }];
    },

    /**
     * 处理集群服务器IP+网卡选择变更
     */
    handleClusterIpNicChange(value, index) {
      if (this.clusterServerIpConfigList[index]) {
        this.clusterServerIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 处理编辑时集群服务器IP+网卡选择变更
     */
    handleEditClusterIpNicChange(value, index) {
      if (this.editClusterServerIpConfigList[index]) {
        this.editClusterServerIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 处理集群部署Node IP+网卡选择变更
     */
    handleClusterNodeIpNicChange(value, index) {
      if (this.clusterNodeIpConfigList[index]) {
        this.clusterNodeIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 处理编辑时集群Node IP+网卡选择变更
     */
    handleEditClusterNodeIpNicChange(value, index) {
      if (this.editClusterNodeIpConfigList[index]) {
        this.editClusterNodeIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 处理部署机IP+网卡选择变更（单台）
     */
    handleClusterDeployIpNicChange(value, index) {
      if (this.clusterDeployIpConfigList[index]) {
        this.clusterDeployIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 处理编辑时部署机IP+网卡选择变更（单台）
     */
    handleEditClusterDeployIpNicChange(value, index) {
      if (this.editClusterDeployIpConfigList[index]) {
        this.editClusterDeployIpConfigList[index].ipWithNic = value;
      }
    },

    /**
     * 从接口获取服务器列表
     */
    getServerList() {
      this.serverListLoading = true;
      this.serverListError = false;
      this.serverList = [];

      httpClient.get('http://127.0.0.1:9091/api/cmdb/server/list')
          .then(res => {
            this.serverListLoading = false;

            if (res.code === 0 && Array.isArray(res.data)) {
              this.serverList = res.data;
            } else {
              this.serverListError = true;
              this.$message.error('服务器列表数据格式错误');
            }
          })
          .catch(err => {
            this.serverListLoading = false;
            this.serverListError = true;
            this.$message.error(`服务器列表加载失败：${err.message || '网络异常'}`);
          });
    },

    /**
     * 从接口获取凭据列表
     */
    getCredentialList() {
      this.credentialListLoading = true;
      this.credentialListError = false;
      this.credentialList = [];

      httpClient.get('http://127.0.0.1:9091/api/cmdb/credential/list')
          .then(res => {
            this.credentialListLoading = false;

            if (res.code === 0 && Array.isArray(res.data)) {
              this.credentialList = res.data;
            } else {
              this.credentialListError = true;
              this.$message.error('凭据列表数据格式错误');
            }
          })
          .catch(err => {
            this.credentialListLoading = false;
            this.credentialListError = true;
            this.$message.error(`凭据列表加载失败：${err.message || '网络异常'}`);
          });
    },

    /**
     * 关闭新建抽屉并重置表单
     */
    handleClose() {
      this.createk8sDrawer = false;

      // 重置错误状态
      this.clusterConfigIncompleteError = false;
      this.haIpError = false;
      this.clusterNodeConfigIncompleteError = false;
      this.clusterDeployConfigIncompleteError = false;

      this.$nextTick(() => {
        if (this.$refs.createDeploymentForm) {
          this.$refs.createDeploymentForm.resetFields();
        }

        // 重置表单默认值
        this.createk8s = {
          hostname: [],
          node_hostname: [],
          deploy_hostname: '',
          k8s_name: '',
          osVersion: 'centos',
          k8sVersion: '',
          clusterServerIds: [],
          haType: 'physical',
          physicalHaIp: "",
          physicalHaNic: "",
          clusterNodeServerIds: [],
          clusterDeployServerId: '',
          imageRepo: 'registry.aliyuncs.com/google_containers',
          networkComponent: 'cilium',
          containerRuntime: 'docker'
        };

        // 重置配置列表
        this.clusterServerIpConfigList = [];
        this.clusterNodeIpConfigList = [];
        this.clusterDeployIpConfigList = [];
      });
    },

    /**
     * 关闭编辑抽屉并重置
     */
    handleEditClose() {
      this.editk8sDrawer = false;
      this.editingCluster = null;

      // 重置错误状态
      this.clusterConfigIncompleteError = false;
      this.haIpError = false;
      this.clusterNodeConfigIncompleteError = false;
      this.clusterDeployConfigIncompleteError = false;

      this.$nextTick(() => {
        if (this.$refs.editDeploymentForm) {
          this.$refs.editDeploymentForm.resetFields();
        }

        // 重置表单数据
        this.editk8s = {
          id: '',
          hostname: [],
          node_hostname: [],
          deploy_hostname: '',
          k8s_name: '',
          osVersion: 'centos',
          k8sVersion: '',
          clusterServerIds: [],
          haType: 'physical',
          physicalHaIp: "",
          physicalHaNic: "",
          clusterNodeServerIds: [],
          clusterDeployServerId: '',
          imageRepo: 'registry.aliyuncs.com/google_containers',
          networkComponent: 'cilium',
          containerRuntime: 'docker'
        };
        this.editClusterServerIpConfigList = [];
        this.editClusterNodeIpConfigList = [];
        this.editClusterDeployIpConfigList = [];
      });
    },

    /**
     * 分页：每页条数变化
     */
    handleSizeChange(size) {
      this.pagesize = size;
      this.currentPage = 1;
    },

    /**
     * 分页：当前页变化
     */
    handleCurrentChange(page) {
      this.currentPage = page;
    },

    /**
     * 提交新建集群表单
     */
    submitCreateK8sForm() {
      // 验证集群部署服务器配置
      if (this.createk8s.clusterServerIds.length === 0) {
        this.$message.error('请选择集群服务器');
        return;
      }
      const allMasterConfigured = this.clusterServerIpConfigList.every(item =>
          item.ipWithNic && item.credentialId
      );
      if (!allMasterConfigured) {
        this.clusterConfigIncompleteError = true;
        this.$message.error('请为所有选中的Master服务器配置IP+网卡和凭据');
        return;
      }

      // 验证部署机配置（单台）
      if (!this.createk8s.clusterDeployServerId) {
        this.$message.error('请选择部署机');
        return;
      }
      const deployConfigured = this.clusterDeployIpConfigList.length > 0 &&
          this.clusterDeployIpConfigList[0].ipWithNic &&
          this.clusterDeployIpConfigList[0].credentialId;
      if (!deployConfigured) {
        this.clusterDeployConfigIncompleteError = true;
        this.$message.error('请为部署机配置IP+网卡和凭据');
        return;
      }

      // 验证高可用配置
      if (this.createk8s.haType === 'physical') {
        const ipReg = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
        if (this.createk8s.physicalHaIp && !ipReg.test(this.createk8s.physicalHaIp)) {
          this.$message.error('物理高可用设备IP地址格式不合法');
          return;
        }
      }

      // 验证Node节点配置
      if (this.createk8s.clusterNodeServerIds.length > 0) {
        const allNodeConfigured = this.clusterNodeIpConfigList.every(item =>
            item.ipWithNic && item.credentialId
        );
        if (!allNodeConfigured) {
          this.clusterNodeConfigIncompleteError = true;
          this.$message.error('请为所有选中的Node服务器配置IP+网卡和凭据');
          return;
        }
      }

      // 表单整体校验
      this.$refs.createDeploymentForm.validate((isValid) => {
        if (isValid) {
          this.appLoading = true;

          // 准备提交数据
          let submitData = {
            ...this.createk8s,
            // 解析IP和网卡信息
            hostname: [],
            node_hostname: [],
            deploy_hostname: '', // 单台部署机主机名
            clusterMasterIps: [],
            clusterMasterNics: [],
            clusterMasterCredentials: [],
            // 集群Node相关字段
            clusterNodeIps: [],
            clusterNodeNics: [],
            clusterNodeCredentials: [],
            // 部署机相关字段（单台）
            clusterDeployIps: [],
            clusterDeployNics: [],
            clusterDeployCredentials: [],
            // 后端需要数组格式，这里统一转换
            clusterDeployServerIds: [this.createk8s.clusterDeployServerId]
          };

          // 处理Master节点数据
          submitData.hostname = [];
          let missingHostname = false;
          this.createk8s.clusterServerIds.forEach(serverId => {
            const server = this.serverList.find(s => s.id === serverId);
            if (server && server.hostname) {
              submitData.hostname.push(server.hostname);
            } else {
              missingHostname = true;
            }
          });
          if (missingHostname) {
            this.$message.error('部分服务器缺少主机名信息，请检查服务器配置');
            this.appLoading = false;
            return;
          }
          this.clusterServerIpConfigList.forEach(item => {
            if (item.ipWithNic) {
              const ipNicInfo = JSON.parse(item.ipWithNic);
              submitData.clusterMasterIps.push(ipNicInfo.ip);
              submitData.clusterMasterNics.push(ipNicInfo.ipName);
              submitData.clusterMasterCredentials.push(item.credentialId);
            }
          });

          // 处理部署机节点数据（单台）
          const deployServer = this.serverList.find(s => s.id === this.createk8s.clusterDeployServerId);
          if (deployServer && deployServer.hostname) {
            submitData.deploy_hostname = [deployServer.hostname];
          } else {
            this.$message.error('部署机缺少主机名信息，请检查服务器配置');
            this.appLoading = false;
            return;
          }
          if (this.clusterDeployIpConfigList[0].ipWithNic) {
            const ipNicInfo = JSON.parse(this.clusterDeployIpConfigList[0].ipWithNic);
            submitData.clusterDeployIps.push(ipNicInfo.ip);
            submitData.clusterDeployNics.push(ipNicInfo.ipName);
            submitData.clusterDeployCredentials.push(this.clusterDeployIpConfigList[0].credentialId);
          }

          // 处理Node节点数据
          if (this.createk8s.clusterNodeServerIds.length > 0) {
            submitData.node_hostname = [];
            let missingNodeHostname = false;
            this.createk8s.clusterNodeServerIds.forEach(serverId => {
              const server = this.serverList.find(s => s.id === serverId);
              if (server && server.hostname) {
                submitData.node_hostname.push(server.hostname);
              } else {
                missingNodeHostname = true;
              }
            });
            if (missingNodeHostname) {
              this.$message.error('部分Node服务器缺少主机名信息，请检查服务器配置');
              this.appLoading = false;
              return;
            }
            this.clusterNodeIpConfigList.forEach(item => {
              if (item.ipWithNic) {
                const ipNicInfo = JSON.parse(item.ipWithNic);
                submitData.clusterNodeIps.push(ipNicInfo.ip);
                submitData.clusterNodeNics.push(ipNicInfo.ipName);
                submitData.clusterNodeCredentials.push(item.credentialId);
              }
            });
          }

          // 处理高可用IP信息
          submitData.physicalHaIp = this.createk8s.physicalHaIp || "null";

          // 调用接口提交
          httpClient.post(common.Create_Deploy_Config, submitData)
              .then(res => {
                this.$message.success('集群创建成功！');
                this.createk8sDrawer = false;
                this.getk8slist();
              })
              .catch(err => {
                this.$message.error(`创建失败：${err.msg || '网络异常'}`);
              })
              .finally(() => {
                this.appLoading = false;
              });
        }
      });
    },

    /**
     * 提交编辑表单
     */
    submitEditK8sForm(create) {
      if (this.isSubmitting) return;
      // 验证Master配置
      if (this.editk8s.clusterServerIds.length === 0) {
        this.$message.error('请选择集群服务器');
        return;
      }
      const allMasterConfigured = this.editClusterServerIpConfigList.every(item =>
          item.ipWithNic && item.credentialId
      );
      if (!allMasterConfigured) {
        this.clusterConfigIncompleteError = true;
        this.$message.error('请为所有选中的Master服务器配置IP+网卡和凭据');
        return;
      }

      // 验证部署机配置（单台）
      if (!this.editk8s.clusterDeployServerId) {
        this.$message.error('请选择部署机');
        return;
      }
      const deployConfigured = this.editClusterDeployIpConfigList.length > 0 &&
          this.editClusterDeployIpConfigList[0].ipWithNic &&
          this.editClusterDeployIpConfigList[0].credentialId;
      if (!deployConfigured) {
        this.clusterDeployConfigIncompleteError = true;
        this.$message.error('请为部署机配置IP+网卡和凭据');
        return;
      }

      // 验证高可用配置
      if (this.editk8s.haType === 'physical') {
        const ipReg = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
        if (this.editk8s.physicalHaIp && !ipReg.test(this.editk8s.physicalHaIp)) {
          this.$message.error('物理高可用设备IP地址格式不合法');
          return;
        }
      }

      // 验证Node节点配置
      if (this.editk8s.clusterNodeServerIds.length > 0) {
        const allNodeConfigured = this.editClusterNodeIpConfigList.every(item =>
            item.ipWithNic && item.credentialId
        );
        if (!allNodeConfigured) {
          this.clusterNodeConfigIncompleteError = true;
          this.$message.error('请为所有选中的Node服务器配置IP+网卡和凭据');
          return;
        }
      }

      // 表单整体校验
      this.$refs.editDeploymentForm.validate((isValid) => {
        if (isValid) {
          this.isSubmitting = true; // 开始提交，禁用按钮
          this.appLoading = true;

          // 准备提交数据
          let submitData = {
            ...this.editk8s,
            clusterMasterIps: [],
            clusterMasterNics: [],
            clusterMasterCredentials: [],
            clusterNodeIps: [],
            clusterNodeNics: [],
            clusterNodeCredentials: [],
            clusterDeployIps: [],
            clusterDeployNics: [],
            clusterDeployCredentials: [],
            node_hostname: [],
            hostname: [],
            deploy_hostname: '',
            // 后端需要数组格式，这里统一转换
            clusterDeployServerIds: [this.editk8s.clusterDeployServerId]
          };

          // 处理Master节点数据
          let missingMasterHostname = false;
          this.editk8s.clusterServerIds.forEach(serverId => {
            const server = this.serverList.find(s => s.id === serverId);
            if (server && server.hostname) {
              submitData.hostname.push(server.hostname);
            } else {
              missingMasterHostname = true;
            }
          });
          if (missingMasterHostname) {
            this.$message.error('部分Master服务器缺少主机名信息');
            this.appLoading = false;
            return;
          }
          this.editClusterServerIpConfigList.forEach(item => {
            if (item.ipWithNic) {
              const ipNicInfo = JSON.parse(item.ipWithNic);
              submitData.clusterMasterIps.push(ipNicInfo.ip);
              submitData.clusterMasterNics.push(ipNicInfo.ipName);
              submitData.clusterMasterCredentials.push(item.credentialId);
            }
          });

          // 处理部署机节点数据（单台）
          const deployServer = this.serverList.find(s => s.id === this.editk8s.clusterDeployServerId);
          if (deployServer && deployServer.hostname) {
            submitData.deploy_hostname = [deployServer.hostname];
          } else {
            this.$message.error('部署机缺少主机名信息');
            this.appLoading = false;
            return;
          }
          if (this.editClusterDeployIpConfigList[0].ipWithNic) {
            const ipNicInfo = JSON.parse(this.editClusterDeployIpConfigList[0].ipWithNic);
            submitData.clusterDeployIps.push(ipNicInfo.ip);
            submitData.clusterDeployNics.push(ipNicInfo.ipName);
            submitData.clusterDeployCredentials.push(this.editClusterDeployIpConfigList[0].credentialId);
          }

          // 处理Node节点数据
          if (this.editk8s.clusterNodeServerIds.length > 0) {
            this.editk8s.clusterNodeServerIds.forEach(serverId => {
              const server = this.serverList.find(s => s.id === serverId);
              if (server && server.hostname) {
                submitData.node_hostname.push(server.hostname);
              }
            });
            this.editClusterNodeIpConfigList.forEach(item => {
              if (item.ipWithNic) {
                const ipNicInfo = JSON.parse(item.ipWithNic);
                submitData.clusterNodeIps.push(ipNicInfo.ip);
                submitData.clusterNodeNics.push(ipNicInfo.ipName);
                submitData.clusterNodeCredentials.push(item.credentialId);
              }
            });
          }

          // 高可用配置
          submitData.physicalHaIp = this.editk8s.physicalHaIp || "null";
          submitData.physicalHaNic = this.editk8s.physicalHaNic || "";


          // 调用更新接口
          httpClient.put(common.Update_Deploy_Config, submitData)
              .then(res => {
                this.$message.success('集群配置更新成功！');
                return httpClient.post(common.Ansible_init, submitData);
              })
              .then(ansibleRes => {
                if (create === 1) {
                  // 关键改动1：给脚本生成接口加5秒超时控制
                  const scriptRequest = httpClient.post(common.GenerateAutomationScript_Install, submitData);
                  // 用Promise.race实现“接口响应”与“5秒超时”的竞争
                  return Promise.race([
                    scriptRequest,
                    new Promise((resolve) => {
                      setTimeout(() => {
                        // 关键改动1秒超时后，主动resolve并标记为“超时成功”
                        resolve({isTimeout: true});
                      }, 1000); // 1000毫秒 = 1秒
                    })
                  ]);
                }
                return Promise.resolve();
              })
              .then(res => {
                // 关键改动3：根据“正常响应”或“超时”统一提示“开始部署”
                if (create === 1) {
                  // 无论接口正常返回还是5秒超时，都提示“部署+脚本开始执行”
                  this.$message.success('集群开始部署且脚本开始执行！');
                } else {
                  this.$message.success('集群开始部署成功！');
                }
                this.editk8sDrawer = false;
                this.getk8slist();
              })
              .catch(err => {
                // 仅当接口在5秒内明确返回错误时，才提示失败
                this.$message.error(`操作失败：${err.msg || '网络异常'}`);
              })
              .finally(() => {
                this.appLoading = false;
                this.isSubmitting = false; // 提交完成，恢复按钮状态
              });
        }
      })
    },


    /**
     * 删除集群
     */
    delk8slist(k8sName,id) {
      this.$confirm(
          `确定要删除集群「${k8sName}」吗？删除后不可恢复！`,
          '警告',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
      ).then(() => {
        this.appLoading = true;
        const delData = {
          url: common.Delete_Deploy_Config,
          id: id
        };

        httpClient.delete(common.Delete_Deploy_Config, { data: delData })
            .then(res => {
              this.$message.success('集群删除成功！');
              this.getk8slist();
            })
            .catch(err => {
              this.$message.error(`删除失败：${err.msg || '网络异常'}`);
            })
            .finally(() => {
              this.appLoading = false;
            });
      }).catch(() => {
        this.$message.info('已取消删除操作');
      });
    },

    /**
     * 获取集群列表
     */
    getk8slist() {
      this.appLoading = true;

      httpClient.get(common.K8s_install_List)
          .then(res => {
            this.k8sList = res.data || [];
          })
          .catch(err => {
            this.$message.error(`获取集群列表失败：${err.msg || '网络异常'}`);
          })
          .finally(() => {
            this.appLoading = false;
          });
    },

    /**
     * 列选择确认
     */
    confirmColumnSelect() {
      // 确保操作列始终选中
      if (!this.selectedColumns.includes('action')) {
        this.selectedColumns.push('action');
      }
      this.saveColumnConfig();
      this.showColumnSelectDialog = false;
    },

    /**
     * 保存列配置到本地存储
     */
    saveColumnConfig() {
      localStorage.setItem('k8sClusterTableColumns', JSON.stringify(this.selectedColumns));
    },

    /**
     * 初始化列配置
     */
    initColumnConfig() {
      const savedColumns = localStorage.getItem('k8sClusterTableColumns');
      if (savedColumns) {
        try {
          this.selectedColumns = JSON.parse(savedColumns);
          // 确保操作列存在
          if (!this.selectedColumns.includes('action')) {
            this.selectedColumns.push('action');
          }
        } catch (e) {
          console.error('Failed to parse saved columns', e);
          this.selectedColumns = Object.keys(this.tableColumns);
        }
      } else {
        // 默认显示所有列
        this.selectedColumns = Object.keys(this.tableColumns);
      }
    },

    /**
     * 打开日志弹窗
     */
    openLogDialog(cluster) {
      this.logDialogVisible = true;
      this.logs = [];
      this.filteredLogs = [];
      this.logSearchKey = '';
      this.currentLogTaskId = cluster.id;
      this.autoScroll = true;

      // 连接WebSocket获取实时日志
      this.connectWebSocket(cluster.k8s_name);
    },

    /**
     * 连接WebSocket
     */
    connectWebSocket(k8s_name) {
      // 关闭已有连接
      if (this.ws) {
        this.ws.close();
      }

      // 创建新连接
      // 1. 获取后端配置的完整 HTTP 日志接口路径（例如："http://127.0.0.1:9091/api/logs/stream"）
      const httpLogBaseUrl = common.GenerateAutomationScript_Install_Log;

      // 2. 解析 HTTP 路径，转换为 WebSocket 协议和路径

        const urlObj = new URL(httpLogBaseUrl); // 用 URL 对象自动解析协议、域名、路径
        const wsProtocol = urlObj.protocol === 'https:' ? 'wss:' : 'ws:'; // http→ws，https→wss
        const wsBasePath = `${urlObj.host}${urlObj.pathname}`; // 拼接域名+端口+基础路径（如 "127.0.0.1:9091/api/logs/stream"）

        // 3. 拼接完整的 WebSocket URL（协议 + 基础路径 + k8s_name）
        const wsUrl = `${wsProtocol}//${wsBasePath}/${k8s_name}`;


      this.ws = new WebSocket(wsUrl);

      this.ws.onopen = () => {
        console.log('WebSocket连接已建立');
        this.isWsConnected = true;
      };

      this.ws.onmessage = (event) => {
        try {
          // 尝试解析 JSON 格式（兼容后端可能的 JSON 推送）
          const data = JSON.parse(event.data);
          if (data.type === 'history' && data.logs) {
            data.logs.forEach(log => this.addLog(log));
          } else if (data.type === 'new' && data.log) {
            this.addLog(data.log);
          } else if (data.code !== 0) {
            this.addLog({
              timestamp: new Date().toISOString(),
              content: `日志服务错误: ${data.msg}`,
              isError: true
            });
          }
        } catch (e) {
          // 解析 JSON 失败，按原始文本日志处理（核心修改）
          console.log('处理原始文本日志:', event.data);
          const logText = event.data;
          // 按换行符分割日志（确保每条日志单独一行）
          const logLines = logText.split('\n').filter(line => line.trim() !== ''); // 过滤空行

          logLines.forEach(line => {
            // 从每行日志中提取时间戳（格式：[2025-11-04 13:43:36]）
            const timeMatch = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]/);
            let timestamp, content;
            if (timeMatch) {
              timestamp = timeMatch[1]; // 提取时间戳
              content = line.replace(timeMatch[0], '').trim(); // 提取日志内容（去除时间戳）
            } else {
              // 无时间戳的日志，用当前时间
              timestamp = new Date().toISOString();
              content = line;
            }

            // 判断是否为错误日志
            const isError = line.includes('失败') || line.includes('error') || line.includes('Error');

            this.addLog({ timestamp, content, isError });
          });
        }
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket错误', error);
        this.isWsConnected = false;
        this.addLog({
          timestamp: new Date().toISOString(),
          content: `日志连接错误: ${error.message || '未知错误'}`,
          isError: true
        });
      };

      this.ws.onclose = () => {
        console.log('WebSocket连接已关闭');
        this.isWsConnected = false;
        this.addLog({
          timestamp: new Date().toISOString(),
          content: '日志流已结束',
          isError: false
        });
      };
    },

    /**
     * 添加日志到列表
     */
    addLog(logData) {
      // 确保日志格式正确
      const log = {
        timestamp: logData.timestamp || new Date().toISOString(),
        content: logData.content || '',
        isError: logData.isError || false
      };

      this.logs.push(log);
      this.filteredLogs = [...this.logs];

      // 应用过滤
      if (this.logSearchKey) {
        this.filterLogs();
      }

      // 自动滚动到底部
      if (this.autoScroll) {
        this.scrollToBottom();
      }
    },

    /**
     * 过滤日志
     */
    filterLogs() {
      if (!this.logSearchKey) {
        this.filteredLogs = [...this.logs];
        return;
      }

      const keyword = this.logSearchKey.toLowerCase();
      this.filteredLogs = this.logs.filter(log =>
          log.content.toLowerCase().includes(keyword)
      );
    },

    /**
     * 清空日志
     */
    clearLogs() {
      this.logs = [];
      this.filteredLogs = [];
    },

    /**
     * 复制所有日志
     */
    copyAllLogs() {
      // 1. 拼接日志文本（保留换行）
      const logText = this.logs
          .map(log => `[${this.formatTime(log.timestamp)}] ${log.content}`)
          .join('\n');

      // 2. 降级方案：创建临时文本框复制
      const textarea = document.createElement('textarea');
      textarea.value = logText;
      textarea.style.position = 'fixed'; // 避免滚动到视图外
      textarea.style.opacity = '0'; // 隐藏文本框
      document.body.appendChild(textarea);

      try {
        // 3. 选中并复制文本
        textarea.select();
        document.execCommand('copy'); // 兼容旧浏览器的复制 API
        this.$message.success('日志已复制到剪贴板');
      } catch (err) {
        console.error('复制失败', err);
        this.$message.error('复制日志失败，请手动复制');
      } finally {
        // 4. 清理临时文本框
        document.body.removeChild(textarea);
      }
    },

    /**
     * 切换自动滚动
     */
    toggleAutoScroll() {
      this.autoScroll = !this.autoScroll;
      if (this.autoScroll) {
        this.scrollToBottom();
      }
    },

    /**
     * 滚动到日志底部
     */
    scrollToBottom() {
      this.$nextTick(() => {
        const logContent = this.$refs.logContent;
        if (logContent) {
          logContent.scrollTop = logContent.scrollHeight;
        }
      });
    },

    /**
     * 格式化时间
     */
    formatTime(timestamp) {
      const date = new Date(timestamp);
      return date.toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false
      }).replace(/\//g, '-');
    },

    /**
     * 关闭日志弹窗
     */
    handleLogDialogClose() {
      if (this.ws) {
        this.ws.close();
        this.ws = null;
        this.isWsConnected = false;
      }
      this.logDialogVisible = false;
      this.currentLogTaskId = '';
      this.logs = [];
      this.filteredLogs = [];
      this.logSearchKey = '';
    }
  },

  mounted() {
    // 初始化表格列配置
    this.initColumnConfig();
    // 获取集群列表
    this.getk8slist();
    // 获取服务器列表
    this.getServerList();
    // 获取凭据列表
    this.getCredentialList();

    // 监听窗口大小变化，调整表格高度
    window.addEventListener('resize', () => {
      this.tableHeight = `calc(100vh - 240px)`;
    });
  },

  beforeDestroy() {
    // 关闭WebSocket连接
    if (this.ws) {
      this.ws.close();
    }
    // 移除窗口大小监听
    window.removeEventListener('resize', () => {
      this.tableHeight = `calc(100vh - 240px)`;
    });
  }
};
</script>

<style scoped>
.k8s-cluster-manager {
  padding: 15px;
  background-color: #f5f7fa;
  height: 100%;
  box-sizing: border-box;
}

.header-row {
  margin-bottom: 15px;
}

.header-card {
  background-color: #fff;
}

.header-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

.create-btn {
  margin-right: 10px;
}

.column-select-btn {
  margin-right: 10px;
}

.search-container {
  display: flex;
  align-items: center;
  gap: 10px;
}

.search-input {
  width: 280px;
}

.table-row {
  height: calc(100% - 80px);
}

.table-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: #fff;
}

.cluster-table {
  width: 100%;
  flex: 1;
}

.pagination {
  margin-top: 15px;
  text-align: right;
}

/* 详情弹窗样式 */
.detail-dialog .el-dialog__body {
  padding: 20px;
  max-height: 70vh;
  overflow-y: auto;
}

/* 表单样式 */
.create-form {
  margin-top: 20px;
}

.form-item {
  margin-bottom: 20px;
}

.form-hint {
  color: #606266;
  font-size: 12px;
  margin-top: 5px;
}

/* 集群服务器配置表格 */
.cluster-server-ip-config {
  margin-top: 10px;
  margin-bottom: 15px;
}

.config-table-container {
  margin-top: 10px;
}

.config-table {
  width: 100%;
}

/* 高可用配置样式 */
.ha-type-container {
  margin-bottom: 15px;
}

.ha-type-radio-group {
  display: flex;
  gap: 15px;
}

.ha-physical-config {
  margin-left: 20px;
}

.ha-physical-item {
  margin-bottom: 15px;
}

.sub-label {
  display: inline-block;
  width: 120px;
  color: #606266;
}

/* 错误提示 */
.validation-error {
  color: #F56C6C;
  font-size: 12px;
  margin-top: 5px;
}

.loading-tip {
  color: #606266;
  font-size: 12px;
  margin-top: 5px;
  display: flex;
  align-items: center;
  gap: 5px;
}

.error-tip {
  color: #F56C6C;
  font-size: 12px;
  margin-top: 5px;
  display: flex;
  align-items: center;
  gap: 5px;
}

/* 列选择弹窗样式 */
.column-checkbox {
  display: block;
  margin-bottom: 10px;
}

/* 日志弹窗样式 */
.log-dialog-container {
  display: flex;
  flex-direction: column;
  height: calc(80vh - 100px);
}

.log-controls {
  display: flex;
  gap: 10px;
  margin-bottom: 15px;
  align-items: center;
}

.log-search {
  flex: 1;
  max-width: 400px;
}

.log-btn {
  white-space: nowrap;
}

.log-content {
  flex: 1;
  overflow-y: auto;
  background-color: #1e1e1e;
  color: #fff;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 14px;
  line-height: 1.5;
}

.log-line {
  margin-bottom: 5px;
  word-break: break-all;
}

.log-time {
  color: #61dafb;
  margin-right: 10px;
}

.log-error {
  color: #ff6b6b;
}

.log-empty {
  color: #aaa;
  text-align: center;
  padding: 20px;
}

/* 其他样式 */
.ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

.text-gray {
  color: #909399;
}

.ip-item {
  margin-bottom: 4px;
}

/* 抽屉样式 */
.create-drawer .el-drawer__body,
.edit-drawer .el-drawer__body {
  padding: 0 20px;
  overflow-y: auto;
}
</style>